package ar.edu.unrn.lia.service.impl;

import java.util.Collections;
import java.util.Date;

import javax.inject.Inject;
import javax.inject.Named;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ar.edu.unrn.lia.dao.AlquilerDAO;
import ar.edu.unrn.lia.model.Alquiler;
import ar.edu.unrn.lia.model.AlquilerCuota;
import ar.edu.unrn.lia.model.AlquilerCuota.CoutaEstado;
import ar.edu.unrn.lia.model.Cuenta;
import ar.edu.unrn.lia.model.CuotaPreIngreso;
import ar.edu.unrn.lia.model.Movimiento;
import ar.edu.unrn.lia.model.Movimiento.TipoMovimiento;
import ar.edu.unrn.lia.model.Recibo;
import ar.edu.unrn.lia.model.Recibo.Concepto;
import ar.edu.unrn.lia.model.ReciboItem;
import ar.edu.unrn.lia.service.AlquilerCuotaService;
import ar.edu.unrn.lia.service.AlquilerService;
import ar.edu.unrn.lia.service.ContableService;
import ar.edu.unrn.lia.service.CuentaService;
import ar.edu.unrn.lia.service.MovimientoService;
import ar.edu.unrn.lia.service.ReciboService;
import ar.edu.unrn.lia.service.TasaInteresService;
import ar.edu.unrn.lia.service.exception.BusinessException;
import ar.edu.unrn.lia.util.FuncionesDate;
import ar.edu.unrn.lia.util.MensajesLenguage;

@Named("contableService")
public class ContableServiceImpl implements ContableService {

	@Inject
	AlquilerDAO entityDAO;

	@Inject
	AlquilerCuotaService alquilerCuotaService;

	@Inject
	CuentaService cuentaService;

	@Inject
	MovimientoService movimientoService;

	@Inject
	AlquilerService alquilerService;

	@Inject
	ReciboService reciboService;
	
	@Inject
	TasaInteresService tasaInteresService;
	

	public ReciboService getReciboService() {
		return reciboService;
	}

	public void setReciboService(ReciboService reciboService) {
		this.reciboService = reciboService;
	}

	public AlquilerService getAlquilerService() {
		return alquilerService;
	}

	public void setAlquilerService(AlquilerService alquilerService) {
		this.alquilerService = alquilerService;
	}

	public CuentaService getCuentaService() {
		return cuentaService;
	}

	public void setCuentaService(CuentaService cuentaService) {
		this.cuentaService = cuentaService;
	}

	public MovimientoService getMovimientoService() {
		return movimientoService;
	}

	public void setMovimientoService(MovimientoService movimientoService) {
		this.movimientoService = movimientoService;
	}

	public AlquilerCuotaService getAlquilerCuotaService() {
		return alquilerCuotaService;
	}

	public void setAlquilerCuotaService(
			AlquilerCuotaService alquilerCuotaService) {
		this.alquilerCuotaService = alquilerCuotaService;
	}

	public AlquilerDAO getEntityDAO() {
		return entityDAO;
	}
	
	


	// Numero de Asiento
		public TasaInteresService getTasaInteresService() {
		return tasaInteresService;
	}

	public void setTasaInteresService(TasaInteresService tasaInteresService) {
		this.tasaInteresService = tasaInteresService;
	}

	public Double intereses(AlquilerCuota alquilerCuota) throws BusinessException 
	{	Double resultado = 0D;
		if (alquilerCuota.estadoPagoParcial() || alquilerCuota.estadoVencida())
			{
				if (alquilerCuota.fechaVencimiento().before(new Date()))
				{
					int cantDias = FuncionesDate.daysBetween(alquilerCuota.fechaVencimiento(),new Date());
					resultado = (tasaInteresFechaHoy()/(30D*100D))*cantDias*alquilerCuota.capitalCalculoVencimiento();
					
				}
			}			
		return resultado;
	}
	
	public Double tasaInteresFechaHoy() throws BusinessException {
			return getTasaInteresService().tasaInteresFechaHoy().getTasa();
	}
	
	// Numero de Asiento
	public Integer ultimoAsiento() {
		return getMovimientoService().ultimoAsiento();
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void pagarCuota(Alquiler alquiler, AlquilerCuota cuota,Integer montoPagar, String usuario) throws BusinessException {	
		
		Integer montoRestante = montoPagar;
		Integer montoPagado;
		Integer asiento = ultimoAsiento();
		Cuenta cuentaInquilino = getCuentaService().getEntityById(alquiler.getInquilino().getCuenta().getId());	
		Cuenta cuentaPropietario = getCuentaService().getEntityById(alquiler.getPropiedad().getPropietario().getCuenta().getId());
	
		Recibo recibo = new Recibo(Concepto.PCA, 
									montoPagar.doubleValue(), 
									new Date(), asiento, alquiler.getInquilino(), usuario, 
									alquiler.getPropiedad().direccionCompleta(),
									Concepto.PCA.toString());
		
		//Tiene pagas Parciales Pendientes		
		if (!alquiler.montoDeudaPagoParcial().equals(0))
		{
			//Pago los pagos Parciales Antes de pagar la Cuota
			for (AlquilerCuota cuotaPP : alquiler.getCuotas()) {
				if (cuotaPP.getEstado().equals(CoutaEstado.PP))
				{
					Integer direfencia=  cuotaPP.getMontoInquilino() - cuotaPP.getMontoInquilinoPagado(); 
					if (direfencia>montoRestante)
					{
						cuotaPP.pagar(montoRestante);
						montoPagado = montoRestante;
						montoRestante = 0;
					}
					else
					{
						cuotaPP.pagar(direfencia);
						montoRestante = montoRestante - direfencia;
						montoPagado = direfencia;
					}
					
					getAlquilerCuotaService().update(cuotaPP);
					
					String concepto = MensajesLenguage.getString("pago.cuota.alquiler", 
																cuotaPP.getEstado().toString(),
																cuotaPP.getNroCuota().toString());
					
					recibo.agregarItem(new ReciboItem(ar.edu.unrn.lia.model.CuotaPreIngreso.Concepto.PA, montoPagado.doubleValue(), concepto));
					asientoSimple(cuentaInquilino, TipoMovimiento.INGRESO, concepto,montoPagar.doubleValue(), asiento);				
				}	
			}
		}	
		
				
		//Pago la cuota seleccionada si me quedo monto despues de pagar lo adeudado parcial
		if (!montoRestante.equals(0))
		{			
			
			//Cuando la Cuota esta Pendiente de Pago, La tarea programada todavia no cambio el estado 
			//y no genero la deuda. Generamos la deuda por el monto de la cuota
			if (cuota.estadoPendientePago())
			{
				asientoSimple(cuentaInquilino, 
						TipoMovimiento.EGRESO, 
						MensajesLenguage.getString("cuota.pago.pendiente",cuota.getNroCuota(),
						FuncionesDate.dateToString(cuota.getFecha())),cuota.getMontoInquilino().doubleValue(), 
						asiento);				
			}
			
			//Pago la Cuota Seleccionada
			cuota.pagar(montoRestante);
			// Actualizar Cuota
			getAlquilerCuotaService().update(cuota);
				
			
			// Generar Movimientos
			String concepto = MensajesLenguage.getString("pago.cuota.alquiler", cuota.getEstado().toString(),cuota.getNroCuota().toString());
			
			recibo.agregarItem(new ReciboItem(ar.edu.unrn.lia.model.CuotaPreIngreso.Concepto.PA, montoRestante.doubleValue(), concepto));			
			//TODO Asiento por partida Doble en Cuento del Propietario (Al propietario se le paga todo con el porcentaje de descuento por la comision) y Caja
			asientoPartidaDoblePropietario(cuentaPropietario,TipoMovimiento.INGRESO,concepto,montoPagar.doubleValue(),cuota.getMontoPropietario().doubleValue(),asiento);
			//Asiento a la Cuenta del Inquilino por el monto pagado.
			asientoSimple(cuentaInquilino, TipoMovimiento.INGRESO, concepto,montoPagar.doubleValue(), asiento);
		}	
		
		getReciboService().save(recibo);
		
		
	}
	
	@Transactional(propagation = Propagation.REQUIRED)
	public void asientoPartidaDoble(Cuenta cuenta, TipoMovimiento tipoMovimiento, String concepto, Double monto, Integer asiento ) throws BusinessException
	{
		asientoSimple(cuenta,tipoMovimiento,concepto,monto,asiento);
		asientoSimple(getCuentaService().obtenerCuentaCaja(),tipoMovimiento,concepto,monto,asiento);
	}
	
	@Transactional(propagation = Propagation.REQUIRED)
	private void asientoPartidaDoblePropietario(Cuenta cuentaPropietario, TipoMovimiento tipoMovimiento, String concepto, Double montoCaja,Double montoPropietario, Integer asiento ) throws BusinessException
	{
		asientoSimple(cuentaPropietario,tipoMovimiento,concepto,montoPropietario,asiento);
		asientoSimple(getCuentaService().obtenerCuentaCaja(),tipoMovimiento,concepto,montoCaja,asiento);
	}
	
	@Transactional(propagation = Propagation.REQUIRED)
	public void asientoSimple(Cuenta cuenta, TipoMovimiento tipoMovimiento, String concepto, Double monto, Integer asiento ) throws BusinessException
	{
		Movimiento mov = new Movimiento(tipoMovimiento, new Date(), concepto,monto, asiento);
		cuenta.AgregarMovimiento(mov);
		getMovimientoService().save(mov);			
	}
	
	
	@Transactional(propagation = Propagation.REQUIRED)
	public void asientoSimple(Cuenta cuenta, TipoMovimiento tipoMovimiento, String concepto, Double monto) throws BusinessException
	{
		asientoSimple(cuenta,tipoMovimiento,concepto,monto,ultimoAsiento());		
	}
	

	@Transactional(propagation = Propagation.REQUIRED)
	public Integer generarMovimientosPreIngreso(Alquiler alquiler) throws BusinessException {
		Cuenta cuentaInquilino = alquiler.getInquilino().getCuenta();
		Cuenta cuentaPropietario = getCuentaService().getEntityById(alquiler.getPropiedad().getPropietario().getCuenta().getId());
		Integer asiento = ultimoAsiento();

		for (CuotaPreIngreso cpre : alquiler.getCuotasPreIngreso()) {
			String 	concepto= MensajesLenguage.getString("cuota.pago.preingreso", 
														cpre.getConcepto().toString(),
														alquiler.getPropiedad().direccionCompleta());
			//TODO Ajuste Alquiler si es negativo, o es positivo
			if (cpre.getConcepto().equals(ar.edu.unrn.lia.model.CuotaPreIngreso.Concepto.A))
			{
				TipoMovimiento tipoMov; 
				//TODO Negativo
				if (cpre.getMonto()<0)
				{
					tipoMov = TipoMovimiento.INGRESO;
				}else 
				{
					tipoMov = TipoMovimiento.EGRESO;
				}
				
				asientoSimple(cuentaInquilino, tipoMov, concepto, Math.abs(cpre.getMonto()), asiento);				
			}
			else
			{
				//si es sellado, hacer asiento al Propietario y Cuenta Rentas
				if (cpre.getConcepto().equals(ar.edu.unrn.lia.model.CuotaPreIngreso.Concepto.S))
				{
					//Movimiento al propietario
					asientoSimple(cuentaPropietario, TipoMovimiento.EGRESO, concepto, cpre.getMonto(), asiento);
					//TODO 148 Movimiento a la Cuenta rentas  
					asientoSimple(getCuentaService().obtenerCuentaRentas(), TipoMovimiento.EGRESO, concepto, cpre.getMonto()*2, asiento);	
				}
				
				asientoSimple(cuentaInquilino, TipoMovimiento.EGRESO, concepto, cpre.getMonto(), asiento);
			}
			
			
		}
		return asiento;
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void pagarCuotaPreIngreso(Alquiler alquiler, Double montoPago,String usuario)
			throws BusinessException {
		Cuenta cuentaInquilino = alquiler.getInquilino().getCuenta();
		Integer asiento = ultimoAsiento();
		Double montoRestante = montoPago;

		// Ordenar lista por prioridad de pago
		Collections.sort(alquiler.getCuotasPreIngreso());
		
		Recibo recibo = new Recibo(Concepto.PPI, montoPago, new Date(), asiento, alquiler.getInquilino(), usuario, alquiler.getPropiedad().direccionCompleta(),
				Concepto.PPI.toString() + "Descripcion");

		// Orden de pago Comision, Deposito, Sellado y Mes Adelantado.
		for (CuotaPreIngreso cPre : alquiler.getCuotasPreIngreso()) {
			Double montoPagadoPorItem = 0D;
			// Sin Monto
			if (montoRestante.equals(0D))
				break;

			// Solo lo que esta Pendiente de pago o Pago Parcial
			if (!cPre.getEstado().equals(CoutaEstado.PT)) {
				// Lo que falta pagar
				Double montoAPagar = cPre.getMonto() - cPre.getMontoPagado();
				if (montoAPagar > montoRestante) {
					cPre.setMontoPagado(cPre.getMontoPagado() + montoRestante);
					montoPagadoPorItem = montoRestante;
					montoRestante = montoRestante - montoRestante;
					cPre.setEstado(CoutaEstado.PP);
				} else {
					cPre.setMontoPagado(cPre.getMontoPagado() + montoAPagar);
					montoPagadoPorItem = montoAPagar;
					montoRestante = montoRestante - montoAPagar;
					cPre.setEstado(CoutaEstado.PT);
				}
				cPre.setFechaPago(new Date());
					
				
				String concepto = MensajesLenguage.getString("pago.cuota.preingreso", cPre.getConcepto().toString(),alquiler.getPropiedad().direccionCompleta());
				
				// Movimiento por partida Doble de Cada Item pagado.
				asientoPartidaDoble(getCuentaService().getEntityById(cuentaInquilino.getId()),TipoMovimiento.INGRESO,concepto,montoPagadoPorItem,asiento);

				recibo.agregarItem(new ReciboItem(cPre.getConcepto(), montoPagadoPorItem, cPre.getConcepto().toString()));
			}

		}
		getReciboService().save(recibo);
		getAlquilerService().update(alquiler);
	}
	
	
	
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void cambiarEstadoCuotaVencida() throws BusinessException
	{		
		//TODO  Enviar Email que recuerde pago de la cuota. Corriendo intereses
		Integer asiento = ultimoAsiento();
		for (AlquilerCuota cuota : getAlquilerCuotaService().cuotasVencidas()) {
			cuota.setEstado(CoutaEstado.V);			
			asientoSimple(cuota.getAlquiler().getInquilino().getCuenta(),
							TipoMovimiento.EGRESO,
							MensajesLenguage.getString("cuota.vencimiento", cuota.getNroCuota(),FuncionesDate.dateToString(cuota.getFecha())), 
							cuota.getMontoInquilino().doubleValue(),
							asiento);
			getAlquilerCuotaService().update(cuota);			
		}
	}
	

}